'''
Created on 01/07/2009

@author: felner
'''
import graph
from cliques.mdp.mdpSolver import smarterCliqueMdpSolver
from cliques.mdp.mdpSolver import cliqueSearchState
from cliques.cliqueFinder import cliqueFinder
from cliques.potentialBasedSearch import kPotentialClique
from cliques.potentialCliques import potentialCliques
from cliques.mdp.potentialBasedState import potentialBasedState

class heuristicMdp(smarterCliqueMdpSolver):
    '''
    This is an implementation of AO* or LAO*, and possibly new algorithms
    '''
    def __init__(self,k,edge_prob, num_of_nodes):
        smarterCliqueMdpSolver.__init__(self, k, edge_prob, num_of_nodes)
        self.openlist = []    
        self.states_per_update = 5
        self.detect_duplicate = True

    def h(self, state):
        return state.pcdb.h

    def create_state_0(self, ckg, generated):
        return potentialBasedState(potentialCliques(self.k, self.search.pcdb),\
                             len(ckg) - len(generated))

    def is_goal(self,state):
        ''' Identify goal with PCDB '''
        if state.pcdb.max_core>=self.k-1:
            return True
        else:
            return False 

    def relevant_possible_neighbors(self,state,action):
        ''' Find all the relevant possible neighbors of the action given the current state '''
        possibles = state.pcdb.find_gcn_members()
        possibles.remove(action)
        return possibles
        

    def generate_next_states(self,state,action):
        ''' Generate the next state possible by applying action to state '''
        ''' Return a generator of 2-tuples containing the state and transition probability '''
        all_possible_neighbors = self.relevant_possible_neighbors(state,action)  
              
        for possible_neighbors in self.find_relevant_neighbors(state, action):               
            new_pcdb = state.pcdb.expand(action, possible_neighbors)
            new_state = self.create_state(new_pcdb,state.g+1)
            pr_new_state = self.pr(action, possible_neighbors,all_possible_neighbors)
            yield (new_state, pr_new_state)               

    def create_state(self,pcdb, g):
        return potentialBasedState(pcdb,g)
        
    def pr(self, node, neighbors, all_possible_neighbors):
        ''' Probability of nodes '''
        return self.edge_model.pr(node, neighbors, all_possible_neighbors) 

    def tr(self, state, action, new_state):
        raise NameError("Unsupported operation")

    def setup(self,ckg,generated):
        smarterCliqueMdpSolver.setup(self, ckg, generated)
        
        self.state_hashtable = dict()
        self.state_hashtable[self.initial.__hash__()]=[self.initial]
    
    def add_state(self, state, v, openlist, moves, action, next_state, next_state_prob):
        ''' Add a new state to the state graph, possibly performing duplicate detection '''
        # Perform duplicate detection if wanted
        if self.detect_duplicate:
            same_state = self.find_same_state(next_state)
        else:
            same_state = None
        
        if same_state is None:
            self.states.add(next_state) # Legacy            
            # Insert new state to hashtable
            hash_value = state.__hash__()
            if self.state_hashtable.has_key(hash_value)==False:
                self.state_hashtable[hash_value]=[state]
            else:
                self.state_hashtable[hash_value].append(state)            
            self.state_graph.add_node(next_state)
            
            # Set value of state and add to openlist
            v[next_state] = next_state.g + self.h(next_state)
            openlist.append(next_state)
            same_state = next_state
            
        self.state_graph.add_edge((state, action), same_state, next_state_prob)

    def find_same_state(self,searched_state):
        ''' Return a state from states that is the same as the given state '''
        hash_value = searched_state.__hash__()
        if self.state_hashtable.has_key(hash_value)==False:
            return None
        else:                
            for state in self.state_hashtable[hash_value]:
                if state.same(searched_state)==True:
                    return state
            return None

    
    
